#include <string>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <sys/stat.h>
#include <fcntl.h>

#define MAX_LINE 1024
#define MAX_ARGC 128

//命令行参数表
char *g_argv[MAX_ARGC];
int g_argc = 0;


#define MAX_ENVS 200
//环境变量表，键值对
char *g_env[MAX_ENVS];
int g_envs = 0;

//上一次程序的退出码
int lastexit = 0;

const char* get_username()
{
    const char* username = getenv((const char*)"USER");
    return username == NULL ? "None" : username;
}

const char* get_hostname()
{
    const char* hostname = getenv((const char*)"HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

const char* get_pwd()
{
    char pwd[MAX_LINE];
    //const char* pwd = getenv((const char*)"PWD");
    std::string PWD = getcwd(pwd,MAX_LINE);
    if(PWD.empty())
    {
        return "None";
    }
    return PWD.c_str();
}

//获取家目录
const char* get_home()
{
    const char* home = getenv((const char*)"HOME");
    return home == NULL ? "None" : home;
}

//获取最后一个路径的名称
const char* GetLastPWD()
{
    std::string pwd = get_pwd();
    //根目录
    if(pwd.size()==1)
        return "/";
    std::string::reverse_iterator rit = pwd.rbegin();
    std::string ret;
    while(rit!=pwd.rend() && *rit!='/')
    {
        ret.push_back(*rit);
        ++rit;
    }
    reverse(ret.begin(),ret.end());
    return ret.c_str();
}

#define MAX_PROMPT 1024
void MakePrompt(char* prompt)
{
#define STYLE "[%s@%s %s]# "
    snprintf(prompt,MAX_PROMPT,STYLE,get_username(),get_hostname(),GetLastPWD());
}

void PrintPrompt()
{
    //1.打印提示符
    char prompt[MAX_PROMPT];
    MakePrompt(prompt);
    printf("%s",prompt);
    fflush(stdout);
}

bool GetInput(char commandline[])
{
    char* ret = fgets(commandline,MAX_LINE,stdin);
    if(ret==NULL)
        return false;
    commandline[strlen(commandline)-1]='\0';
    return true;
}

bool SpliceString(char commandline[])
{ 
#define space " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline,space);
    while((bool)(g_argv[g_argc++]=strtok(NULL,space)));
    g_argv[g_argc] = NULL;
    --g_argc;
    return true;
}

void CD()
{
    std::string env = "PWD";
    //修改当前路径为家目录，修改环境变量
    if(strcmp(g_argv[1],"~")==0)
    {
        const char* home = get_home();
        chdir(home);
        setenv(env.c_str(),home,1);
        return;
    }
    //修改当前工作路径，修改成功，修改环境变量PWD
    else if(chdir(g_argv[1])==0)
    {
        setenv(env.c_str(),g_argv[1],1);
    }
}

void ECHO()
{
    std::string content = g_argv[1];
    if(content[0]=='$')
    {
        //上一次程序退出码
        if(content=="$?")
        {
            std::cout << lastexit << std::endl;
        }
        //打印环境变量的值
        else
        {
            //获得环境变量的键值
            std::string env_key(content.begin()+1,content.end());
            const char* env_value = getenv(env_key.c_str());
            std::cout << env_value << std::endl;
        }
    }
    else
    {
        std::cout << g_argv[1] << std::endl;
    }
}

bool CheckBuiltIncommand()
{
    if(strcmp(g_argv[0],"cd")==0)
    {
        CD();
        lastexit = 0;
        return true;
    }
    else if (strcmp(g_argv[0],"echo")==0)
    {
        ECHO();
        lastexit = 0;
        return true;
    }
    return false;
}



void InitEnv()
{
    //从全局的environ读取环境变量，即从父进程bash读取
    extern char **environ;
    g_envs=0;
    //遍历环境变量，添加到自身环境变量表中
    for(int i = 0;environ[i];i++)
    {
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        memcpy(g_env[i],environ[i],strlen(environ[i])+1);
        ++g_envs;
    }
    //添加
    const char *myenv = "MYVAL=12345678";
    g_env[g_envs] = (char*)malloc(strlen(myenv)+1);
    memcpy(g_env[g_envs],myenv,strlen(myenv)+1);
    ++g_envs;
    g_env[g_envs] = NULL;
    for(int i = 0;g_env[i];i++)
    {
        putenv(g_env[i]);
    }
}

#define INPUT 0
#define OUTPUT 1
#define APPEND 2

//模式
int g_mode = -1;
std::string filename;

bool CheckRedirect(char cmd[],int len)
{
    g_mode = -1;
    int rbegin = len-1;
    while(rbegin>=0)
    {
        //输入重定向
        if(cmd[rbegin]=='<')
        {
            g_mode = INPUT;
        }
        else if(cmd[rbegin]=='>')
        {
            //追加重定向
            if(rbegin-1>=0 && cmd[rbegin-1]=='>')
            {
                g_mode = APPEND;
            }
            //输出重定向
            else
            {
                g_mode = OUTPUT;
            }
        }
        else
        {
            --rbegin;
        }
        //统一处理
        if(g_mode!=-1)
        {
            //分割命令与追加符号、文件名
            //追加时，"<<"前一个'<'要置成'\0'
            if(g_mode==APPEND)
                cmd[rbegin-1] = '\0';
            cmd[rbegin] = '\0';
            //跳过空格，获取文件名
            ++rbegin;
            while(rbegin<len)
            {
                if(cmd[rbegin]==' ')
                    ++rbegin;
                else
                    break;
            }
            filename = cmd+rbegin;
            return true;
        }
    }
    //没有重定向符号
    return false;
}

void Excute()
{
    pid_t pid = fork();
    if(pid==0)
    {
        if(g_mode==INPUT)
        {
            int fd = open(filename.c_str(),O_RDONLY);
            dup2(fd,0);
        }
        else if(g_mode==OUTPUT)
        {
            int fd = open(filename.c_str(),O_CREAT | O_TRUNC | O_WRONLY,0666);
            dup2(fd,1);
        }
        else if(g_mode==APPEND)
        {
            int fd = open(filename.c_str(),O_CREAT | O_WRONLY |O_APPEND,0666);
            dup2(fd,1);
        }
        execvp(g_argv[0],g_argv);
    }
    int status;
    waitpid(pid,&status,0);
    lastexit = WEXITSTATUS(status);
}

int main()
{
    InitEnv();
    char commandline[MAX_LINE];
    memset(commandline,0,MAX_LINE);
    while(true)
    {
        //1.打印提示符
        PrintPrompt();
        //2.获取输入的字符串
        if(!GetInput(commandline) || strlen(commandline)==0)
            continue;
        //3.检查重定向符号
        CheckRedirect(commandline,strlen(commandline));
        //4.以空格为分割，分割字符串,存到命令行参数表中
        if(!SpliceString(commandline))
            continue;
        //5.检查是否是内建命令，若是由父进程执行
        if(CheckBuiltIncommand())
            continue;
        //6.创建子进程，子进程程序替换执行命令
        Excute();
    }
    return 0;
}
